2024_stockhausen_helicopter_string_quartet.py

#

SPDX-FileCopyrightText: 2025 Hana Chibani & Yasmina El youmni SPDX-FileCopyrightText: 2025 AlICe laboratory https://alicelab.be

SPDX-License-Identifier: GPL-3.0-or-later

import bpy
import bmesh
import random
import math
#

NETTOYAGE

bpy.ops.object.select_all(action="SELECT")
bpy.ops.object.delete()
bpy.ops.outliner.orphans_purge()
#
Make a custom plane with a set number of faces.
The idea is to have a plane with subsections to be
move afterwards.
def make_plane(nbr_faces, obj_name="custom_plane", size=0.1, z_pos=0.0):
#

Calcule de la taille

    length = size
    step = length / (nbr_faces)
#

CALCULER LES COORDONNEES À PARTIR DE LA LONGUEUR ET DE LA LARGEUR

    nbr_faces += 1
#

créer une liste de coordonnées de sommets

    row1 = [(x * step, 0, z_pos) for x in range(nbr_faces)]
    row2 = [(x * step, size, z_pos) for x in range(nbr_faces)]
    vert_coords = row1 + row2
#

créer une liste d’indices de sommets qui font partie d’une face donnée

    face_vert_indices = [
        (i, i + 1, i + nbr_faces + 1, i + nbr_faces) for i in range(nbr_faces - 1)
    ]
#

faire un mesh créer les données du mesh

    mesh_data = bpy.data.meshes.new(f"{obj_name}_data")
#

créer l’objet sur base des données du mesh

    mesh_obj = bpy.data.objects.new(obj_name, mesh_data)
#

ajouter le mesh a la scene

    bpy.context.scene.collection.objects.link(mesh_obj)
#

créer un nouveau mesh

    bm = bmesh.new()
#

créer et ajouter des sommets

    for coord in vert_coords:
        bm.verts.new(coord)
    bm.verts.ensure_lookup_table()
    for vert_indices in face_vert_indices:
        bm.faces.new([bm.verts[index] for index in vert_indices])
#

writes the bmesh data into the mesh data

    bm.to_mesh(mesh_data)
#

nettoyer la memoire qui etait attrivuer pour le mesh

    bm.free()

    return bpy.data.objects[obj_name]
#
def set_origin_to_geometry(obj_name):
    bpy.ops.object.select_all(action="DESELECT")
    bpy.data.objects[obj_name].select_set(True)
    bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN")
#

faces

def move_face(plane_name, face_index, z_val):
#
Prenez mon objet plan personnalisé et déplacez l'une des faces en z.

arguments : - plane_name : sélectionnez la plane par son nom - face_index : la clé d’index du visage à déplacer en z - z_val = une valeur z à donner aux 4 sommets de la face sélectionnée

    my_plane = bpy.data.objects[plane_name]

    nbr_faces = len(my_plane.data.vertices) // 2

    my_plane.data.vertices[face_index].co[2] += z_val
    my_plane.data.vertices[face_index + nbr_faces].co[2] += z_val

    my_plane.data.vertices[face_index + 1].co[2] += z_val
    my_plane.data.vertices[face_index + nbr_faces + 1].co[2] += z_val
#

cotés

def move_edge(plane_name, edge_index, z_val):
#
Prenez mon plan personnalisé Mobject et déplacez l'une des faces en z.

arguments: - plane_name : sélectionnez la plane par son nom - face_index : la clé d’index du visage à déplacer en z - z_val = une valeur z à donner aux 4 sommets de la face sélectionnée

    my_plane = bpy.data.objects[plane_name]

    nbr_faces = len(my_plane.data.vertices) // 2

    my_plane.data.vertices[edge_index].co[2] += z_val
    my_plane.data.vertices[edge_index + nbr_faces].co[2] += z_val
#

Alto

make_plane(nbr_faces=20, obj_name="first_plane", z_pos=0.01)
move_edge("first_plane", edge_index=1, z_val=0.02)
move_edge("first_plane", edge_index=3, z_val=0.02)
move_edge("first_plane", edge_index=5, z_val=0.02)
move_edge("first_plane", edge_index=7, z_val=0.02)
move_edge("first_plane", edge_index=9, z_val=0.02)
move_edge("first_plane", edge_index=11, z_val=0.02)
move_edge("first_plane", edge_index=13, z_val=0.02)
move_edge("first_plane", edge_index=15, z_val=0.02)
move_edge("first_plane", edge_index=17, z_val=0.02)
move_edge("first_plane", edge_index=19, z_val=0.02)
set_origin_to_geometry(obj_name="first_plane")
#

violoncelle

make_plane(nbr_faces=20, obj_name="second_plane", z_pos=0.06)
move_edge("second_plane", edge_index=1, z_val=0.01)
move_edge("second_plane", edge_index=2, z_val=0.04)
move_edge("second_plane", edge_index=3, z_val=-0.04)
move_edge("second_plane", edge_index=4, z_val=0.01)
move_edge("second_plane", edge_index=6, z_val=0.01)
move_edge("second_plane", edge_index=8, z_val=0.01)
move_edge("second_plane", edge_index=9, z_val=0.04)
move_edge("second_plane", edge_index=10, z_val=-0.04)
move_edge("second_plane", edge_index=11, z_val=0.01)
move_edge("second_plane", edge_index=13, z_val=0.01)
move_edge("second_plane", edge_index=15, z_val=0.01)
move_edge("second_plane", edge_index=16, z_val=0.04)
move_edge("second_plane", edge_index=17, z_val=-0.04)
move_edge("second_plane", edge_index=18, z_val=0.01)
move_edge("second_plane", edge_index=20, z_val=0.01)
set_origin_to_geometry(obj_name="second_plane")
#

Violon 1

make_plane(nbr_faces=20, obj_name="third_plane", z_pos=0.05)
move_edge("third_plane", edge_index=0, z_val=0.01)
move_edge("third_plane", edge_index=1, z_val=0.01)
move_edge("third_plane", edge_index=3, z_val=0.01)
move_edge("third_plane", edge_index=5, z_val=0.01)
move_edge("third_plane", edge_index=6, z_val=0.01)
move_edge("third_plane", edge_index=7, z_val=0.01)
move_edge("third_plane", edge_index=9, z_val=0.01)
move_edge("third_plane", edge_index=11, z_val=0.01)
move_edge("third_plane", edge_index=12, z_val=0.01)
move_edge("third_plane", edge_index=13, z_val=0.01)
move_edge("third_plane", edge_index=15, z_val=0.01)
move_edge("third_plane", edge_index=17, z_val=0.01)
move_edge("third_plane", edge_index=18, z_val=0.01)
move_edge("third_plane", edge_index=19, z_val=0.01)
set_origin_to_geometry(obj_name="third_plane")
bpy.data.objects["third_plane"].rotation_euler[1] = 1.5708
bpy.data.objects["third_plane"].location[0] = 0.01  # Align on Y-axis
#

violon2

make_plane(nbr_faces=20, obj_name="four_plane", z_pos=0.05)
move_edge("four_plane", edge_index=0, z_val=0.01)
move_edge("four_plane", edge_index=1, z_val=0.01)
move_edge("four_plane", edge_index=3, z_val=0.01)
move_edge("four_plane", edge_index=5, z_val=0.01)
move_edge("four_plane", edge_index=7, z_val=0.01)
move_edge("four_plane", edge_index=8, z_val=0.01)
move_edge("four_plane", edge_index=9, z_val=0.01)
move_edge("four_plane", edge_index=11, z_val=0.01)
move_edge("four_plane", edge_index=13, z_val=0.01)
move_edge("four_plane", edge_index=15, z_val=0.01)
move_edge("four_plane", edge_index=16, z_val=0.01)
move_edge("four_plane", edge_index=17, z_val=0.01)
move_edge("four_plane", edge_index=19, z_val=0.01)
set_origin_to_geometry(obj_name="four_plane")
bpy.data.objects["four_plane"].rotation_euler[1] = 1.5708
bpy.data.objects["four_plane"].location[0] = 0.09  # Align on Y-axis
#

création des formes verticales bis

def make_plane(
    nbr_faces, obj_name="custom_plane", size=0.1, x_pos=0.0, z_pos=0.0, y_pos=0.0
):
#
Créez un plane personnalisé avec un nombre défini de faces.

L’idée est d’avoir un plane avec des sous-sections à déménagé par la suite.

Calcule de la taille

    length = size
    step = length / (nbr_faces)
#

CALCULER LES COORDONNEES À PARTIR DE LA LONGUEUR ET DE LA LARGEUR

    nbr_faces += 1
#

créer une liste de coordonnées de sommets

    row1 = [(x * step, 0, z_pos) for x in range(nbr_faces)]
    row2 = [(x * step, size, z_pos) for x in range(nbr_faces)]
    vert_coords = row1 + row2
#

créer une liste d’indices de sommets qui font partie d’une face donnée

    face_vert_indices = [
        (i, i + 1, i + nbr_faces + 1, i + nbr_faces) for i in range(nbr_faces - 1)
    ]
#

faire un mesh créer les données du mesh

    mesh_data = bpy.data.meshes.new(f"{obj_name}_data")
#

créer l’objet sur base des données du mesh

    mesh_obj = bpy.data.objects.new(obj_name, mesh_data)
#

ajouter l’objet dans la scène

    bpy.context.scene.collection.objects.link(mesh_obj)
#

créer un nouveau mesh

    bm = bmesh.new()
#

crer et ahouter des vertices (sommets)

    for coord in vert_coords:
        bm.verts.new(coord)
    bm.verts.ensure_lookup_table()
    for vert_indices in face_vert_indices:
        bm.faces.new([bm.verts[index] for index in vert_indices])
#

writes the bmesh data into the mesh data

    bm.to_mesh(mesh_data)
#

nettoyer la memoire qui etait attrivuer pour le mesh

    bm.free()

    return bpy.data.objects[obj_name]
#
def set_origin_to_geometry(obj_name):
    bpy.ops.object.select_all(action="DESELECT")
    bpy.data.objects[obj_name].select_set(True)
    bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY", center="MEDIAN")
#
Déplacez l'un des cotés d'un  plane personnalisé.

Arguments : - plane_name : sélectionnez la plane par son nom - edge_index : la clé d’index du bord à déplacer - z_val, y_val, x_val : valeurs pour ajuster la position des sommets

def move_edge(plane_name, edge_index, z_val, y_val=0.0, x_val=0.0):
#
    my_plane = bpy.data.objects[plane_name]
    nbr_faces = len(my_plane.data.vertices) // 2
    my_plane.data.vertices[edge_index].co[2] += z_val
    my_plane.data.vertices[edge_index + nbr_faces].co[2] += z_val
#

creation et modification des planes Alto bis

make_plane(nbr_faces=20, obj_name="five_plane", z_pos=0.05)
move_edge("five_plane", edge_index=3, z_val=0.02)
move_edge("five_plane", edge_index=3, z_val=0.02)
move_edge("five_plane", edge_index=5, z_val=0.02)
move_edge("five_plane", edge_index=7, z_val=0.02)
move_edge("five_plane", edge_index=9, z_val=0.02)
move_edge("five_plane", edge_index=11, z_val=0.02)
move_edge("five_plane", edge_index=13, z_val=0.02)
move_edge("five_plane", edge_index=15, z_val=0.02)
move_edge("five_plane", edge_index=17, z_val=0.02)
move_edge("five_plane", edge_index=19, z_val=0.02)
set_origin_to_geometry(obj_name="five_plane")
bpy.data.objects["five_plane"].rotation_euler[0] = 1.5708  # Rotate 90 degrees around X
bpy.data.objects["five_plane"].location[1] = 0.09  # Align on Y-axis
#

violoncelle bis

make_plane(nbr_faces=20, obj_name="six_plane", z_pos=0.05)
move_edge("six_plane", edge_index=1, z_val=0.01)
move_edge("six_plane", edge_index=2, z_val=0.04)
move_edge("six_plane", edge_index=3, z_val=-0.04)
move_edge("six_plane", edge_index=4, z_val=0.01)
move_edge("six_plane", edge_index=6, z_val=0.01)
move_edge("six_plane", edge_index=8, z_val=0.01)
move_edge("six_plane", edge_index=9, z_val=0.04)
move_edge("six_plane", edge_index=10, z_val=-0.04)
move_edge("six_plane", edge_index=11, z_val=0.01)
move_edge("six_plane", edge_index=13, z_val=0.01)
move_edge("six_plane", edge_index=15, z_val=0.01)
move_edge("six_plane", edge_index=16, z_val=0.04)
move_edge("six_plane", edge_index=17, z_val=-0.04)
move_edge("six_plane", edge_index=18, z_val=0.01)
move_edge("six_plane", edge_index=20, z_val=0.01)
set_origin_to_geometry(obj_name="six_plane")
bpy.data.objects["six_plane"].rotation_euler[0] = 1.5708  # Rotate 90 degrees around X
bpy.data.objects["six_plane"].location[1] = 0.045  # Align on Y-axis
#

violon1 bis

make_plane(nbr_faces=20, obj_name="seven_plane", z_pos=0.05)
move_edge("seven_plane", edge_index=0, z_val=0.01)
move_edge("seven_plane", edge_index=1, z_val=0.01)
move_edge("seven_plane", edge_index=3, z_val=0.01)
move_edge("seven_plane", edge_index=5, z_val=0.01)
move_edge("seven_plane", edge_index=6, z_val=0.01)
move_edge("seven_plane", edge_index=7, z_val=0.01)
move_edge("seven_plane", edge_index=9, z_val=0.01)
move_edge("seven_plane", edge_index=11, z_val=0.01)
move_edge("seven_plane", edge_index=12, z_val=0.01)
move_edge("seven_plane", edge_index=13, z_val=0.01)
move_edge("seven_plane", edge_index=15, z_val=0.01)
move_edge("seven_plane", edge_index=17, z_val=0.01)
move_edge("seven_plane", edge_index=18, z_val=0.01)
move_edge("seven_plane", edge_index=19, z_val=0.01)
set_origin_to_geometry(obj_name="seven_plane")
bpy.data.objects["seven_plane"].rotation_euler[0] = 1.5708
bpy.data.objects["seven_plane"].location[1] = 0.07  # Align on Y-axis
#

violon2 bis

make_plane(nbr_faces=20, obj_name="eigt_plane", z_pos=0.05)
move_edge("eigt_plane", edge_index=0, z_val=0.01)
move_edge("eigt_plane", edge_index=1, z_val=0.01)
move_edge("eigt_plane", edge_index=3, z_val=0.01)
move_edge("eigt_plane", edge_index=5, z_val=0.01)
move_edge("eigt_plane", edge_index=7, z_val=0.01)
move_edge("eigt_plane", edge_index=8, z_val=0.01)
move_edge("eigt_plane", edge_index=9, z_val=0.01)
move_edge("eigt_plane", edge_index=11, z_val=0.01)
move_edge("eigt_plane", edge_index=13, z_val=0.01)
move_edge("eigt_plane", edge_index=15, z_val=0.01)
move_edge("eigt_plane", edge_index=16, z_val=0.01)
move_edge("eigt_plane", edge_index=17, z_val=0.01)
move_edge("eigt_plane", edge_index=19, z_val=0.01)
set_origin_to_geometry(obj_name="eigt_plane")
bpy.data.objects["eigt_plane"].rotation_euler[0] = 1.5708
bpy.data.objects["eigt_plane"].location[1] = 0.01  # Align on Y-axis
#

fonction qui permet d’extruder les objets

def extrude_and_solidify(obj_name, thickness=0.05):
#
Extrudez et appliquez un modificateur de solidification pour donner de l'épaisseur à un objet.

Paramètres :

obj_name (str) : Le nom de l’objet à modifier. épaisseur (flottant) : L’épaisseur à appliquer avec le modificateur de solidification.

    obj = bpy.data.objects.get(obj_name)
    if not obj:
        print(f"Object '{obj_name}' not found!")
        return
#

Définir l’objet en mode actif et édition

    bpy.context.view_layer.objects.active = obj
    bpy.ops.object.mode_set(mode="EDIT")
    bm = bmesh.from_edit_mesh(obj.data)
#

extruder la geometrie selectionnée

    geom = bm.faces[:]
    ret = bmesh.ops.extrude_face_region(bm, geom=geom)
    verts_extruded = [v for v in ret["geom"] if isinstance(v, bmesh.types.BMVert)]
    bmesh.ops.translate(bm, verts=verts_extruded, vec=(0, 0, 0.003))
#

Mettez à jour le BMesh et revenez en mode objet

    bmesh.update_edit_mesh(obj.data)
    bpy.ops.object.mode_set(mode="OBJECT")
#

Appliquer le modificateur de solidification pour l’épaisseur

    solidify_mod = obj.modifiers.new(name="Solidify", type="SOLIDIFY")
    solidify_mod.thickness = thickness
    bpy.ops.object.modifier_apply(modifier=solidify_mod.name)
#

appliquer les fontctions aux planes

extrude_and_solidify("first_plane", thickness=0.000)
extrude_and_solidify("second_plane", thickness=0.00)
extrude_and_solidify("third_plane", thickness=0.00)
extrude_and_solidify("four_plane", thickness=0.00)
extrude_and_solidify("five_plane", thickness=0.000)
extrude_and_solidify("six_plane", thickness=0.00)
extrude_and_solidify("seven_plane", thickness=0.00)
extrude_and_solidify("eigt_plane", thickness=0.00)
#

creations des voix du fragement voix une

bpy.ops.mesh.primitive_uv_sphere_add(radius=0.01, location=(0.01, 0.01, 0.020))
sphere = bpy.context.object
sphere.name = "movable_sphere"
set_origin_to_geometry(obj_name="movable_sphere")
#

voix deux

bpy.ops.mesh.primitive_uv_sphere_add(radius=0.015, location=(0.09, 0.09, 0.090))
sphere = bpy.context.object
sphere.name = "movable_sphere"
set_origin_to_geometry(obj_name="movable_sphere")
#

partie random du code

def randomize_size(obj_name, min_size=0.05, max_size=1):
#
Randomiser la taille d'un objet plan personnalisé en modifiant son échelle.
Arguments :
  • obj_name : Le nom de l’objet à modifier.
  • min_size : La taille minimale (facteur d’échelle) pour la randomisation.
  • max_size : La taille maximale (facteur d’échelle) pour la randomisation
    obj = bpy.data.objects.get(obj_name)
    if obj:
#

Generate random scale values within the given range for X, Y, and Z

        random_scale = random.uniform(min_size, max_size)
        obj.scale = (random_scale, random_scale, random_scale)
#

Apply random size variation to the planes

randomize_size("first_plane", min_size=0.1, max_size=1)
randomize_size("second_plane", min_size=0.1, max_size=1)
randomize_size("third_plane", min_size=0.1, max_size=1)
randomize_size("four_plane", min_size=0.1, max_size=1)
randomize_size("five_plane", min_size=0.1, max_size=1)
randomize_size("six_plane", min_size=0.1, max_size=1)
randomize_size("seven_plane", min_size=0.1, max_size=1)
randomize_size("eigt_plane", min_size=0.1, max_size=1)
#

Apply random size variation to the “bis” planes

randomize_size("first_plane_bis", min_size=0.1, max_size=1)
randomize_size("second_plane_bis", min_size=0.1, max_size=1)
randomize_size("third_plane_bis", min_size=0.1, max_size=1)
randomize_size("four_plane_bis", min_size=0.1, max_size=1)
randomize_size("five_plane_bis", min_size=0.1, max_size=1)
randomize_size("six_plane_bis", min_size=0.1, max_size=1)
randomize_size("seven_plane_bis", min_size=0.1, max_size=1)
randomize_size("eigt_plane_bis", min_size=0.1, max_size=1)